/*
 * NetUtil.cpp
 *
 *  Created on: 2012-12-14
 *      Author: yangentao@gmail.com
 */

#include "NetUtil.h"
#include "SocketError.h"

#include <WinSock2.h>
#include <windows.h>

#include <exception>
#include "../SparseArray.h"
#pragma comment(lib, "ws2_32.lib")

namespace yet {

u_long getIp4(string hostname) {
	struct hostent* h = gethostbyname(hostname.c_str());
	if (h && h->h_addrtype == AF_INET) {
		return *(u_long *) h->h_addr_list[0];
	}
	return 0;
}

FdSet::FdSet() {
	zero();
}
FdSet::FdSet(SOCKET s) {
	zero();
	set(s);
}
FdSet::~FdSet() {

}
void FdSet::zero() {
	FD_ZERO(&fdset);
}
void FdSet::set(SOCKET s) {
	FD_SET(s, &fdset);
}
bool FdSet::isSet(SOCKET s) {
	return 0 != FD_ISSET(s, &fdset);
}
void FdSet::remove(SOCKET s) {
	FD_CLR(s, &fdset);
}

InetAddr::InetAddr() {
	memset(&addr_in, 0, sizeof(addr_in));
	addr_in.sin_family = AF_INET;
}
InetAddr::InetAddr(u_short port) {
	memset(&addr_in, 0, sizeof(addr_in));
	addr_in.sin_family = AF_INET;
	setPort(port);
}
InetAddr::InetAddr(string host, u_short port) {
	memset(&addr_in, 0, sizeof(addr_in));
	addr_in.sin_family = AF_INET;
	setIp(host);
	setPort(port);
}
InetAddr::InetAddr(u_long ip, u_short port) {
	memset(&addr_in, 0, sizeof(addr_in));
	addr_in.sin_family = AF_INET;
	setIp(ip);
	setPort(port);
}

bool InetAddr::isEmptyIp() {
	return getIpLong()== 0;
}

void InetAddr::setPort(u_short port) {
	addr_in.sin_port = htons(port);
}
void InetAddr::setPortAny(){
	setPort(0);
}
void InetAddr::setHost(string host){
	setIp(host);
}
void InetAddr::setIp(string host) {
	assert(!host.empty());
	if (isalpha(host.at(0))) {
		u_long ip = getIp4(host);
		setIp(ip);
	} else {
		setIp(inet_addr(host.c_str()));
	}
}
void InetAddr::setIp(u_long ip) {
	addr_in.sin_addr.s_addr = ip;
}
void InetAddr::setIpBroadcast() {
	setIp(INADDR_BROADCAST);
}
u_long InetAddr::getIpLong() {
	return addr_in.sin_addr.s_addr;
}
string InetAddr::getIP() {
	string s = inet_ntoa(addr_in.sin_addr);
	return s;
}
short InetAddr::getPort() {
	return ntohs(addr_in.sin_port);
}

struct sockaddr* InetAddr::getAddr() {
	return (struct sockaddr*) &addr_in;
}
int InetAddr::getSize() {
	return sizeof(addr_in);
}

void throwSocketError() {
	SocketError e;
	e.print();
	throw e;
}
void throwSocketError(int code) {
	SocketError e(code);
	e.print();
	throw e;
}
void throwSocketErrorIf(bool cond) {
	if (cond) {
		SocketError e;
		e.print();
		throw e;
	}
}

static void initSocketErrors(SparseArray<string>& s);

SockLib::SockLib() {
}

SockLib::~SockLib() {

}

void SockLib::init() {
	AutoLock lock(this);
	WSAData data;
	if (0 != WSAStartup(WINSOCK_VERSION, &data)) {
		throw exception("WSAStartup Error!");
	}
}
void SockLib::clean() {
	AutoLock lock(this);
	WSACleanup();
}

AutoSockLib::AutoSockLib() {
	socklib.init();
}
AutoSockLib::~AutoSockLib() {
	socklib.clean();
}

string getSocketErrorCodeName(int code) {
	static SparseArray<string> s;
	static ThreadLock tlock;
	if (s.isEmpty()) {
		AutoLock al(tlock);
		if (s.isEmpty()) {
			initSocketErrors(s);
		}
	}
	return s.get(code, "");
}

static void initSocketErrors(SparseArray<string>& s) {
	s.put(WSA_INVALID_HANDLE, "WSA_INVALID_HANDLE");
	s.put(WSA_NOT_ENOUGH_MEMORY, "WSA_NOT_ENOUGH_MEMORY");
	s.put(WSA_INVALID_PARAMETER, "WSA_INVALID_PARAMETER");
	s.put(WSA_OPERATION_ABORTED, "WSA_OPERATION_ABORTED");
	s.put(WSA_IO_INCOMPLETE, "WSA_IO_INCOMPLETE");
	s.put(WSA_IO_PENDING, "WSA_IO_PENDING");
	s.put(WSAEINTR, "WSAEINTR");
	s.put(WSAEBADF, "WSAEBADF");
	s.put(WSAEFAULT, "WSAEFAULT");
	s.put(WSAEINVAL, "WSAEINVAL");
	s.put(WSAEMFILE, "WSAEMFILE");
	s.put(WSAEWOULDBLOCK, "WSAEWOULDBLOCK");
	s.put(WSAEINPROGRESS, "WSAEINPROGRESS");
	s.put(WSAEALREADY, "WSAEALREADY");
	s.put(WSAENOTSOCK, "WSAENOTSOCK");
	s.put(WSAEDESTADDRREQ, "WSAEDESTADDRREQ");
	s.put(WSAEMSGSIZE, "WSAEMSGSIZE");
	s.put(WSAEPROTOTYPE, "WSAEPROTOTYPE");
	s.put(WSAENOPROTOOPT, "WSAENOPROTOOPT");
	s.put(WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT");
	s.put(WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT");
	s.put(WSAEOPNOTSUPP, "WSAEOPNOTSUPP");
	s.put(WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT");
	s.put(WSAEADDRINUSE, "WSAEADDRINUSE");
	s.put(WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL");
	s.put(WSAENETDOWN, "WSAENETDOWN");
	s.put(WSAENETUNREACH, "WSAENETUNREACH");
	s.put(WSAENETRESET, "WSAENETRESET");
	s.put(WSAECONNABORTED, "WSAECONNABORTED");
	s.put(WSAECONNRESET, "WSAECONNRESET");
	s.put(WSAENOBUFS, "WSAENOBUFS");
	s.put(WSAEISCONN, "WSAEISCONN");
	s.put(WSAENOTCONN, "WSAENOTCONN");
	s.put(WSAESHUTDOWN, "WSAESHUTDOWN");
	s.put(WSAETOOMANYREFS, "WSAETOOMANYREFS");
	s.put(WSAETIMEDOUT, "WSAETIMEDOUT");
	s.put(WSAECONNREFUSED, "WSAECONNREFUSED");
	s.put(WSAELOOP, "WSAELOOP");
	s.put(WSAENAMETOOLONG, "WSAENAMETOOLONG");
	s.put(WSAEHOSTDOWN, "WSAEHOSTDOWN");
	s.put(WSAEHOSTUNREACH, "WSAEHOSTUNREACH");
	s.put(WSAENOTEMPTY, "WSAENOTEMPTY");
	s.put(WSAEPROCLIM, "WSAEPROCLIM");
	s.put(WSAEUSERS, "WSAEUSERS");
	s.put(WSAEDQUOT, "WSAEDQUOT");
	s.put(WSAESTALE, "WSAESTALE");
	s.put(WSAEREMOTE, "WSAEREMOTE");
	s.put(WSASYSNOTREADY, "WSASYSNOTREADY");
	s.put(WSAVERNOTSUPPORTED, "WSAVERNOTSUPPORTED");
	s.put(WSANOTINITIALISED, "WSANOTINITIALISED");
	s.put(WSAEDISCON, "WSAEDISCON");
	s.put(WSAENOMORE, "WSAENOMORE");
	s.put(WSAECANCELLED, "WSAECANCELLED");
	s.put(WSAEINVALIDPROCTABLE, "WSAEINVALIDPROCTABLE");
	s.put(WSAEINVALIDPROVIDER, "WSAEINVALIDPROVIDER");
	s.put(WSAEPROVIDERFAILEDINIT, "WSAEPROVIDERFAILEDINIT");
	s.put(WSASYSCALLFAILURE, "WSASYSCALLFAILURE");
	s.put(WSASERVICE_NOT_FOUND, "WSASERVICE_NOT_FOUND");
	s.put(WSATYPE_NOT_FOUND, "WSATYPE_NOT_FOUND");
	s.put(WSA_E_NO_MORE, "WSA_E_NO_MORE");
	s.put(WSA_E_CANCELLED, "WSA_E_CANCELLED");
	s.put(WSAEREFUSED, "WSAEREFUSED");
	s.put(WSAHOST_NOT_FOUND, "WSAHOST_NOT_FOUND");
	s.put(WSATRY_AGAIN, "WSATRY_AGAIN");
	s.put(WSANO_RECOVERY, "WSANO_RECOVERY");
	s.put(WSANO_DATA, "WSANO_DATA");
	s.put(WSA_QOS_RECEIVERS, "WSA_QOS_RECEIVERS");
	s.put(WSA_QOS_SENDERS, "WSA_QOS_SENDERS");
	s.put(WSA_QOS_NO_SENDERS, "WSA_QOS_NO_SENDERS");
	s.put(WSA_QOS_NO_RECEIVERS, "WSA_QOS_NO_RECEIVERS");
	s.put(WSA_QOS_REQUEST_CONFIRMED, "WSA_QOS_REQUEST_CONFIRMED");
	s.put(WSA_QOS_ADMISSION_FAILURE, "WSA_QOS_ADMISSION_FAILURE");
	s.put(WSA_QOS_POLICY_FAILURE, "WSA_QOS_POLICY_FAILURE");
	s.put(WSA_QOS_BAD_STYLE, "WSA_QOS_BAD_STYLE");
	s.put(WSA_QOS_BAD_OBJECT, "WSA_QOS_BAD_OBJECT");
	s.put(WSA_QOS_TRAFFIC_CTRL_ERROR, "WSA_QOS_TRAFFIC_CTRL_ERROR");
	s.put(WSA_QOS_GENERIC_ERROR, "WSA_QOS_GENERIC_ERROR");
	s.put(WSA_QOS_ESERVICETYPE, "WSA_QOS_ESERVICETYPE");
	s.put(WSA_QOS_EFLOWSPEC, "WSA_QOS_EFLOWSPEC");
	s.put(WSA_QOS_EPROVSPECBUF, "WSA_QOS_EPROVSPECBUF");
	s.put(WSA_QOS_EFILTERSTYLE, "WSA_QOS_EFILTERSTYLE");
	s.put(WSA_QOS_EFILTERTYPE, "WSA_QOS_EFILTERTYPE");
	s.put(WSA_QOS_EFILTERCOUNT, "WSA_QOS_EFILTERCOUNT");
	s.put(WSA_QOS_EOBJLENGTH, "WSA_QOS_EOBJLENGTH");
	s.put(WSA_QOS_EFLOWCOUNT, "WSA_QOS_EFLOWCOUNT");
	s.put(WSA_QOS_EUNKOWNPSOBJ, "WSA_QOS_EUNKOWNPSOBJ");
	s.put(WSA_QOS_EPOLICYOBJ, "WSA_QOS_EPOLICYOBJ");
	s.put(WSA_QOS_EFLOWDESC, "WSA_QOS_EFLOWDESC");
	s.put(WSA_QOS_EPSFLOWSPEC, "WSA_QOS_EPSFLOWSPEC");
	s.put(WSA_QOS_EPSFILTERSPEC, "WSA_QOS_EPSFILTERSPEC");
	s.put(WSA_QOS_ESDMODEOBJ, "WSA_QOS_ESDMODEOBJ");
	s.put(WSA_QOS_ESHAPERATEOBJ, "WSA_QOS_ESHAPERATEOBJ");
	s.put(WSA_QOS_RESERVED_PETYPE, "WSA_QOS_RESERVED_PETYPE");
}

} /* namespace yet */
